﻿using Autofac;
using Autofac.Core;
using Autofac.Core.Resolving.Pipeline;
using AutoRegistDependency.Abstract;
using AutoRegistDependency.Attributes;
using AutoRegistDependency.Attributes.Processor;
using AutoRegistDependency.Enum;
using AutoRegistDependency.Interface;
using AutoRegistDependency.Utils;
using Castle.DynamicProxy;
using Microsoft.Extensions.Configuration;
using Quartz;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;

namespace AutoRegistDependency.Component
{
    /// <summary>
    /// 组件信息类
    /// </summary>
    public class ComponentDefinition
    {
        /// <summary>
        /// ctor
        /// </summary>
        public ComponentDefinition()
        {
        }

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="type">注册的类型</param>
        /// <param name="globalLifeTimeType">全局生命周期</param>
        /// <param name="globalPropertyInjectType">全局属性注入类型</param>
        /// <param name="globalRegistrationType">全局注册类型</param>
        /// <exception cref="ArgumentException"></exception>
        public ComponentDefinition(Type type, LifeTimeType globalLifeTimeType,
            PropertyInjectType globalPropertyInjectType, RegistrationType globalRegistrationType)
        {
            if (type.GetCustomAttributes().Count(t => t.GetType().GetCustomAttribute<UniqueAttribute>() != null) > 1)
            {
                throw new ArgumentException(
                    $"{type.FullName} :In one class can not own two of ComponentAttribute,ConfigurationAttribute,ConfigurationPropertiesAttribute and JobAttribute!!");
            }

            //获取所有ICondition接口的特性
            Conditions = AutoFacHelper.GetAttributeInstance<ICondition>(type).ToList();
            Conditions.AddRange(GetConditions(type.GetCustomAttribute<ConditionalAttribute>()));
            OrderAttribute orderAttribute = AutoFacHelper.GetAttributeInstance<OrderAttribute>(type).FirstOrDefault();
            Order = orderAttribute?.Order ?? 999;
            ComponentAttribute component = type.GetCustomAttribute<ComponentAttribute>();
            ConfigurationAttribute configurationAttribute = type.GetCustomAttribute<ConfigurationAttribute>();
            ConfigurationPropertiesAttribute configurationPropertiesAttribute =
                type.GetCustomAttribute<ConfigurationPropertiesAttribute>();
            JobAttribute jobAttribute = type.GetCustomAttribute<JobAttribute>();
            AggregateServiceAttribute aggregateServiceAttribute = type.GetCustomAttribute<AggregateServiceAttribute>();
            InjectType = type;
            var ctors = type.GetConstructors().Where(t => t.GetCustomAttribute<ConstructorAttribute>() != null)
                .ToList();
            if (ctors.Count() > 1)
            {
                throw new ArgumentException(
                    $"{type.FullName}:ConstructorAttribute supports only one constructor in the same class!!");
            }

            if (ctors.Any())
            {
                ConstructorInfo = ctors.First();
            }

            Name = type.Name;
            if (component != null)
            {
                var dependsOn = InjectType.GetCustomAttributes<DependsOnAttribute>();
                DependsOns.AddRange(dependsOn);
                //bean增强器处理
                if (typeof(IBeanPostProcessor).IsAssignableFrom(InjectType))
                {
                    RegisterAs = new Type[] { typeof(IBeanPostProcessor) };
                    ComponentType = ComponentType.BeanPostProcessor;
                    Lifetime = LifeTimeType.SingleInstance;
                }
                else
                {
                    Name = string.IsNullOrWhiteSpace(component.Name) ? type.Name : component.Name;
                    Key = component.Key;
                    AutoActivate = component.AutoActivate;
                    PropertyInjectType = component.PropertyInjectType == PropertyInjectType.Default
                        ? globalPropertyInjectType
                        : component.PropertyInjectType;
                    RegistType = component.RegistrationType == RegistrationType.Default
                        ? globalRegistrationType
                        : component.RegistrationType;
                    Lifetime = component.Lifetime == LifeTimeType.Default?globalLifeTimeType:component.Lifetime;
                    if (type.IsSubclassOf(typeof(AbstractInterceptor)))
                    {
                        ComponentType = ComponentType.Interceptor;
                        RegisterAs = new Type[] { InjectType };
                    }
                    else
                    {
                        ComponentType = ComponentType.Component;
                    }

                    GetRegistrationAs(InjectType.GetCustomAttribute<RegistrationAsAttribute>());
                    GetEventBusInterfaces();
                }
            }

            if (configurationAttribute != null)
            {
                if (type.IsGenericType)
                {
                    throw new ArgumentException(
                        $"{InjectType.FullName} :ConfigurationAttribute not support GenericType!");
                }

                ComponentType = ComponentType.Configuration;
                RegisterAs = new Type[] { InjectType };
                Lifetime = LifeTimeType.SingleInstance;
            }

            if (configurationPropertiesAttribute != null)
            {
                ComponentType = ComponentType.ConfigurationProperties;
                RegisterAs = new Type[] { InjectType };
                Lifetime = LifeTimeType.SingleInstance;
            }

            if (jobAttribute != null)
            {
                if (InjectType.IsGenericType)
                {
                    throw new ArgumentException($"{InjectType.FullName} :JobAttribute not support GenericType!");
                }

                if (!typeof(IJob).IsAssignableFrom(InjectType))
                {
                    throw new ArgumentException($"{InjectType.FullName} : Must be Implement IJob!!");
                }
                else
                {
                    Lifetime = jobAttribute.LifeTime ?? globalLifeTimeType;
                    ComponentType = ComponentType.Job;
                    RegistType = RegistrationType.Self;
                    Cron = jobAttribute.Cron;
                    Priority = jobAttribute.Priority;
                    RegisterAs = new Type[] { InjectType };
                }
            }

            if (aggregateServiceAttribute != null)
            {
                Lifetime = aggregateServiceAttribute.Lifetime ?? globalLifeTimeType;
                ComponentType = ComponentType.AggregateService;
                RegisterAs = new Type[] { InjectType };
            }

            InitInjectTypeInfo(globalLifeTimeType, globalPropertyInjectType, globalRegistrationType);
        }

        /// <summary>
        /// 注入的源类型
        /// </summary>
        public Type InjectType { get; private set; }

        /// <summary>
        /// 指定的构造函数
        /// </summary>
        public ConstructorInfo ConstructorInfo { get; private set; }

        /// <summary>
        /// 构造函数参数
        /// </summary>
        public Type[] ConstructorTypes
        {
            get
            {
                if (ConstructorInfo == null)
                {
                    return Type.EmptyTypes;
                }

                return ConstructorInfo.GetParameters().Select(t => t.ParameterType).ToArray();
            }
        }

        /// <summary>
        /// 拦截器类型
        /// </summary>
        public HashSet<AbstractInterceptor> Interceptors { get; set; } = new HashSet<AbstractInterceptor>();

        /// <summary>
        /// 自动装配的属性
        /// </summary>
        public List<PropertyInfo> AutowiredProperties { get; private set; } = new List<PropertyInfo>();

        /// <summary>
        /// 自动装配的字段
        /// </summary>
        public List<FieldInfo> AutowiredFields { get; private set; } = new List<FieldInfo>();

        /// <summary>
        /// 自动装配的方法
        /// </summary>
        public List<MethodInfo> AutowireMethods { get; private set; } = new List<MethodInfo>();

        /// <summary>
        /// 自动读取的配置项属性
        /// </summary>
        public List<PropertyInfo> ValueProperties { get; private set; } = new List<PropertyInfo>();

        /// <summary>
        /// 自动读取的配置项字段
        /// </summary>
        public List<FieldInfo> ValueFields { get; private set; } = new List<FieldInfo>();

        /// <summary>
        /// 组件依赖特性
        /// </summary>
        public List<DependsOnAttribute> DependsOns { get; private set; } = new List<DependsOnAttribute>();

        /// <summary>
        /// 生命周期
        /// </summary>
        public LifeTimeType Lifetime { get; private set; }

        /// <summary>
        /// 是否自动激活实例
        /// </summary>
        public bool AutoActivate { get; }

        /// <summary>
        /// 组件name
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// 排序
        /// </summary>
        public int Order { get; private set; }

        /// <summary>
        /// 自动装配类型
        /// </summary>
        public PropertyInjectType PropertyInjectType { get; private set; }

        /// <summary>
        /// 注册方法
        /// </summary>
        public RegistrationType RegistType { get; private set; }

        /// <summary>
        /// 组件key
        /// </summary>
        public object Key { get; private set; }

        /// <summary>
        /// 组件注册条件
        /// </summary>
        public List<ICondition> Conditions { get; private set; } = new List<ICondition>();

        /// <summary>
        /// 注册目标类型
        /// </summary>
        public Type[] RegisterAs { get; private set; }

        /// <summary>
        /// 注册类实现的事件总线接口
        /// </summary>
        public List<Type> EventBusInterfaces { get; set; } = new List<Type>();

        /// <summary>
        /// 实例调用构造函数时执行的方法
        /// </summary>
        public List<MethodInfo> OnActivatingMethods { get; private set; } = new List<MethodInfo>();

        /// <summary>
        /// 实例初始化完成之后执行的方法
        /// </summary>
        public List<MethodInfo> OnActivatedMethods { get; private set; } = new List<MethodInfo>();

        /// <summary>
        /// 实例销毁之前执行的方法
        /// </summary>
        public List<MethodInfo> OnReleaseMethods { get; private set; } = new List<MethodInfo>();

        /// <summary>
        /// 组件类型
        /// </summary>
        internal ComponentType ComponentType { get; private set; }

        /// <summary>
        /// 类内注册bean的方法(只有在confignature特性内才会扫描此特性)
        /// </summary>
        public List<BeanDefinition> BeanDefinitions { get; private set; } = new List<BeanDefinition>();

        /// <summary>
        /// 定时任务cron表达式
        /// </summary>
        public string Cron { get; private set; }

        /// <summary>
        /// 定时任务优先级
        /// </summary>
        public int Priority { get; private set; }

        /// <summary>
        /// 获取注册目标
        /// </summary>
        /// <param name="registAs"></param>
        /// <exception cref="ArgumentException"></exception>
        protected void GetRegistrationAs(RegistrationAsAttribute registAs)
        {
            var interfaces = AutoFacHelper.GetImplInterfaces(InjectType);
            if (registAs != null)
            {
                foreach (Type type in registAs.Types)
                {
                    //如果注册的目标类型不是接口 也不是类 直接抛出异常
                    if (!type.IsInterface && !type.IsClass)
                    {
                        throw new ArgumentException($"{InjectType} registAs must be class or interface!!!");
                    }

                    //非泛型判断是不是实现了接口 或者继承目标类以及自己
                    if (!InjectType.IsGenericType)
                    {
                        if (!(interfaces.Contains(type) || InjectType.IsSubclassOf(type) || InjectType != type))
                        {
                            throw new ArgumentException($"{this.InjectType} is can not convert to {type.Name}!");
                        }
                    }
                    //泛型判断原型是否相同
                    else
                    {
                        if (!(interfaces.Select(t => t.GetGenericTypeDefinition()).Contains(type) ||
                              type.GetGenericTypeDefinition() == InjectType.GetGenericTypeDefinition()))
                        {
                            throw new ArgumentException($"{this.InjectType} is can not convert to {type.Name}!");
                        }
                    }
                }

                RegisterAs = registAs.Types;
            }
            else
            {
                switch (RegistType)
                {
                    case RegistrationType.Interface:
                        RegisterAs = interfaces.ToArray();
                        break;
                    case RegistrationType.Self:
                        RegisterAs = new Type[] { InjectType };
                        break;
                    case RegistrationType.All:
                        var types = interfaces.ToList();
                        types.Add(InjectType);
                        RegisterAs = types.ToArray();
                        break;
                    default:
                        throw new ArgumentException($"{InjectType.FullName}: RegistType can not be support!");
                }
            }
        }

        /// <summary>
        /// 获取所有的ICondtional实现
        /// </summary>
        /// <param name="conditionalAttribute"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        protected List<ICondition> GetConditions(ConditionalAttribute conditionalAttribute)
        {
            List<ICondition> list = new List<ICondition>();
            if (conditionalAttribute == null)
            {
                return list;
            }
            else
            {
                var conditions = conditionalAttribute.ConditionTypes;
                foreach (Type conditionType in conditions)
                {
                    if (!typeof(ICondition).IsAssignableFrom(conditionType)
                        && conditionType.IsClass
                        && conditionType.IsAbstract)
                    {
                        throw new ArgumentException(
                            $"{conditionType.Name} must not be an abstract class and not implement ICondition!");
                    }

                    var ctor = conditionType.GetConstructors().OrderBy(t => t.GetParameters().Length).FirstOrDefault();
                    object[] parameters = new object[ctor.GetParameters().Length];
                    for (int i = 0; i < ctor.GetParameters().Length; i++)
                    {
                        parameters[i] = AutoFacHelper.GetParameterDefaultValue(ctor.GetParameters()[i]);
                    }

                    ICondition conditionInstance = (ICondition)ctor.Invoke(parameters);
                    list.Add(conditionInstance);
                }

                return list;
            }
        }

        /// <summary>
        /// 获取当前类实现的事件总线接口
        /// </summary>
        protected void GetEventBusInterfaces()
        {
            var interfaces = InjectType.GetInterfaces();
            EventBusInterfaces = interfaces.Where(t => t.IsGenericType
                                                       && (t.GetGenericTypeDefinition() == typeof(IEventHandler<>) ||
                                                           t.GetGenericTypeDefinition() == typeof(IEventHandler<,>)
                                                           || t.GetGenericTypeDefinition() ==
                                                           typeof(IEventAsyncHandler<>) ||
                                                           t.GetGenericTypeDefinition() ==
                                                           typeof(IEventAsyncHandler<,>)))
                .ToList();
        }

        private void InitInjectTypeInfo(LifeTimeType globalLifeTimeType, PropertyInjectType globalPropertyInjectType,
            RegistrationType globalRegistType)
        {
            foreach (var prop in InjectType.GetProperties(
                         BindingFlags.Public |
                         BindingFlags.NonPublic |
                         BindingFlags.Static |
                         BindingFlags.Instance |
                         BindingFlags.DeclaredOnly))
            {
                var autowired = prop.GetCustomAttribute<AutowiredAttribute>();
                var value = prop.GetCustomAttribute<ValueAttribute>();
                if (autowired != null && value != null)
                {
                    throw new ArgumentException(
                        $"{InjectType.FullName}:AutowiredAttribute and ValueAttribute can not decorate same property!");
                }

                if (autowired != null)
                {
                    AutowiredProperties.Add(prop);
                }

                if (value != null)
                {
                    ValueProperties.Add(prop);
                }
            }

            foreach (var field in InjectType.GetFields(
                         BindingFlags.Public |
                         BindingFlags.NonPublic |
                         BindingFlags.Static |
                         BindingFlags.Instance |
                         BindingFlags.DeclaredOnly))
            {
                var autowired = field.GetCustomAttribute<AutowiredAttribute>();
                var value = field.GetCustomAttribute<ValueAttribute>();
                if (autowired != null && value != null)
                {
                    throw new ArgumentException(
                        $"{InjectType.FullName}:AutowiredAttribute and ValueAttribute can not decorate same property!");
                }

                if (autowired != null)
                {
                    AutowiredFields.Add(field);
                }

                if (value != null)
                {
                    ValueFields.Add(field);
                }
            }

            foreach (var method in InjectType.GetMethods(
                         BindingFlags.Public |
                         BindingFlags.NonPublic |
                         BindingFlags.Static |
                         BindingFlags.Instance |
                         BindingFlags.DeclaredOnly))
            {
                var autowire = method.GetCustomAttribute<AutowiredAttribute>();
                if (autowire != null)
                {
                    AutowireMethods.Add(method);
                }

                var beforeConstruct = method.GetCustomAttribute<OnActivatingAttribute>();
                if (beforeConstruct != null)
                {
                    OnActivatingMethods.Add(method);
                }

                var postConstruct = method.GetCustomAttribute<OnActivatedAttribute>();
                if (postConstruct != null)
                {
                    OnActivatedMethods.Add(method);
                }

                var preDestroy = method.GetCustomAttribute<OnReleaseAttribute>();
                if (preDestroy != null)
                {
                    if (method.GetParameters().Length != 0)
                    {
                        throw new ArgumentException("PreDestory method must be non parameter!!");
                    }

                    OnReleaseMethods.Add(method);
                }

                if (ComponentType == ComponentType.Configuration || ComponentType == ComponentType.Component)
                {
                    var bean = method.GetCustomAttribute<BeanAttribute>();
                    if (bean != null)
                    {
                        if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task))
                        {
                            throw new ArgumentException(
                                $"{InjectType.FullName}:BeanAttribute can not support returnType is void or Task!");
                        }
                        else
                        {
                            var beanDefinition = new BeanDefinition()
                            {
                                Name = string.IsNullOrWhiteSpace(bean.Name) ? method.ReturnType.Name : bean.Name,
                                Key = bean.Key,
                                LocationType = InjectType,
                                BeanCreatedMethod = method,
                                ComponentType = ComponentType.Bean,
                                RegistType = RegistrationType.All,
                                PropertyInjectType = PropertyInjectType.Autowired,
                                Conditions = AutoFacHelper.GetAttributeInstance<ICondition>(method).ToList(),
                                Order = AutoFacHelper.GetAttributeInstance<OrderAttribute>(method).FirstOrDefault()
                                    ?.Order ?? 999,
                                DependsOns = method.GetCustomAttributes<DependsOnAttribute>().ToList(),
                            };
                            //获取所有ICondition接口的特性
                            beanDefinition.Conditions.AddRange(
                                GetConditions(method.GetCustomAttribute<ConditionalAttribute>()));
                            if (beanDefinition.IsAsync)
                            {
                                beanDefinition.InjectType = method.ReturnType.GetGenericArguments()[0];
                            }
                            else
                            {
                                beanDefinition.InjectType = method.ReturnType;
                            }

                            beanDefinition.Lifetime = bean.LifeTimeType==LifeTimeType.Default? globalLifeTimeType:bean.LifeTimeType;
                            beanDefinition.GetRegistrationAs(method.GetCustomAttribute<RegistrationAsAttribute>());
                            BeanDefinitions.Add(beanDefinition);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 执行属性注入
        /// </summary>
        /// <param name="context"></param>
        public void InjectAction(ResolveRequestContext context)
        {
            var instance = context.Instance;
            ResolveParameter resolveParameter = null;

            if (context.Parameters != null)
            {
                resolveParameter = (ResolveParameter)context.Parameters.FirstOrDefault(t => t is ResolveParameter);
            }

            if (resolveParameter == null)
            {
                resolveParameter = new ResolveParameter();
            }

            //缓存组件服务 解决属性注入的循环依赖
            resolveParameter.AddService(context.Service, instance);
            AutowiredProperties?.ForEach(prop =>
                prop?.GetCustomAttribute<AutowiredAttribute>()
                    ?.ExecutePropertyAutowiredInject(instance, prop, resolveParameter, context));
            AutowiredFields?.ForEach(field =>
                field?.GetCustomAttribute<AutowiredAttribute>()
                    ?.ExecuteFieldAutowiredInject(instance, field, resolveParameter, context));
            ValueProperties?.ForEach(prop =>
                prop?.GetCustomAttribute<ValueAttribute>()?.ExecutePropertyValueInject(instance, prop, context));
            ValueFields?.ForEach(field =>
                field?.GetCustomAttribute<ValueAttribute>()?.ExecuteFieldValueInject(instance, field, context));
            AutowireMethods?.ForEach(method =>
                method?.GetCustomAttribute<AutowiredAttribute>()
                    ?.ExecuteMethodAutowiredInject(instance, method, context));
        }

        /// <summary>
        /// 将实例转为配置文件模型
        /// </summary>
        /// <param name="context"></param>
        public void ConvertToConfigValue(ResolveRequestContext context)
        {
            ConfigurationPropertiesAttribute confignatrueAttribute =
                InjectType.GetCustomAttribute<ConfigurationPropertiesAttribute>();
            IConfiguration configuration = context.Resolve<IConfiguration>();
            object value = configuration.GetSection(confignatrueAttribute.Path).Get(context.Instance.GetType());
            context.Instance = value;
        }

        /// <summary>
        /// 获取当前类中所有的注册信息
        /// </summary>
        /// <returns></returns>
        public List<ComponentDefinition> GetComponentTypeRegistInfos()
        {
            List<ComponentDefinition> result = new List<ComponentDefinition>
            {
                this
            };
            result.AddRange(BeanDefinitions);
            return result;
        }
    }

    /// <summary>
    /// bean注册信息
    /// </summary>
    public class BeanDefinition : ComponentDefinition
    {
        /// <summary>
        /// bean所在的类
        /// </summary>
        public Type LocationType { get; set; }

        /// <summary>
        /// 创建bean的方法
        /// </summary>
        public MethodInfo BeanCreatedMethod { get; set; }

        /// <summary>
        /// 是否异步
        /// </summary>
        public bool IsAsync => BeanCreatedMethod.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;

        /// <summary>
        /// 是否为创建bean的方法是否为泛型
        /// </summary>
        public bool IsGeneric => BeanCreatedMethod.IsGenericMethod && !IsAsync;

        /// <summary>
        /// 是否能注册此方法
        /// </summary>
        public bool CanRegist
        {
            get
            {
                if (IsGeneric)
                {
                    if (!InjectType.IsGenericType)
                    {
                        return false;
                    }

                    if (BeanCreatedMethod.GetGenericArguments().Length != InjectType.GetGenericArguments().Length)
                    {
                        return false;
                    }

                    return true;
                }
                else
                {
                    return true;
                }
            }
        }
    }
}